namespace Math
{


template <typename T, typename K>
StructDataVector<T,K>::~StructDataVector()
  {
      DeleteSecondInstanceOfCoord();
      DeleteFirstInstanceOfCoord(DELETE::HARD);
  }    



template <typename T, typename K>
StructDataVector<T,K>::StructDataVector(const StructDataVector<T,K>& copy):
  ref_or_copy(copy.Getref_or_copy())
  {
      coord = new T*[4];
      if(ref_or_copy == RC::COPY)
      {    
          FillAndCopy((copy.Getcoord())[0]);

      } else
           {
               T** help_coord = copy.Getcoord();
               coord[X] = help_coord[X];
               coord[Y] = help_coord[Y];
               coord[Z] = help_coord[Z];
               coord[W] = help_coord[W];
           }
  }



template <typename T, typename K>
StructDataVector<T,K>::StructDataVector(StructDataVector<T,K>&& move):
  ref_or_copy(move.Getref_or_copy())
  {
      coord      = move.coord;
      move.coord = nullptr;
  }



template <typename T, typename K>
StructDataVector<T,K>::StructDataVector():
  StructDataVector<T,K>((T)0,(T)0,(T)0,(T)0)
  {
  }



template <typename T, typename K>
StructDataVector<T,K>::StructDataVector(const T* const vec):
  ref_or_copy(RC::COPY)
  {
      coord = new T*[4];
      FillAndCopy(vec);
  }



template <typename T, typename K>
StructDataVector<T,K>::StructDataVector(T* vec, RC c):
  ref_or_copy(c)
  {
      coord = new T*[4];
      if(c == RC::COPY)
      {
          FillAndCopy(vec);
      } else 
           {
               ushorti i;
               for(i = 0; i < 4; i++) coord[i] = vec + i;
           }
  }



template <typename T, typename K>
StructDataVector<T,K>::StructDataVector(T* x, T* y, T* z, T* w, RC c):
  ref_or_copy(c)
  {
      coord = new T*[4];
      if(c == RC::COPY)
      {    
           FillCoordByNewT();

           (*coord[X]) = *x;
           (*coord[Y]) = *y;
           (*coord[Z]) = *z;
           (*coord[W]) = *w;

      } else
           {
               coord[X] = x;
               coord[Y] = y;
               coord[Z] = z;
               coord[W] = w;
           }
  }



template <typename T, typename K>
StructDataVector<T,K>::StructDataVector(T x, T y, T z, T w):
  ref_or_copy(RC::COPY)
  {
       coord = new T*[4];
       FillCoordByNewT();

      (*coord[X]) = x;
      (*coord[Y]) = y;
      (*coord[Z]) = z;
      (*coord[W]) = w;
  }

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////// operators decralation ////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

template <typename T, typename K>
T& StructDataVector<T,K>::operator[](ushorti c)
  {
      if(c >= 4) throw E_Math::IndexIsTooHighStructDataVector<T,K>(this, "T& StructDataVector<T,K>::operator[](XYZW c)\n");

      return (*coord[c]);
  }



template <typename T, typename K>
const T& StructDataVector<T,K>::operator[](ushorti c) const
  {
      if(c >= 4) throw E_Math::IndexIsTooHighStructDataVector<T,K>(this, "const T& StructDataVector<T,K>::operator[](XYZW c) const\n");

      return (*coord[c]);
  }



template <typename T, typename K>
StructDataVector<T,K>::operator T*()
  {
      T* help_pointer = new T[4];

      ushorti i;
      for(i = 0; i < 4; i++) help_pointer[i] = (*coord[i]);

      return help_pointer;
  }



template <typename T, typename K>
StructDataVector<T,K>::operator const T*() const
  {
      T* help_pointer = new T[4];

      ushorti i;
      for(i = 0; i < 4; i++) help_pointer[i] = (*coord[i]);

      return help_pointer;
  }

//////////////////////////////////////////////////////// Friend operators //////////////////////////////////////////////

template <typename T, typename K>
ostream& operator<<(ostream& output, const StructDataVector<T,K>& right)
  {
      cout << "(";
      ushorti i;
      for(i = 0; i < 4; i++) 
      {
          output << right[(XYZW)i];
          if(i < 3) cout << ", ";
      }
      output << ")";

      return output;
  }


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////// protected functions///////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

template <typename T, typename K>
void StructDataVector<T,K>::CopyToCoord(T** vec)
  {
      ushorti i;
      for(i = 0; i < 4; i++) (*coord[i]) = (*vec[i]);
  }



template <typename T, typename K>
void StructDataVector<T,K>::CopyToCoord(const T* const vec)
  {
      ushorti i;
      for(i = 0; i < 4; i++) (*coord[i]) = vec[i];
  }



template <typename T, typename K>
void StructDataVector<T,K>::FillCoordByNewT()
  {
      T* help_pointer = new T[4];

      ushorti i;
      for(i = 0; i < 4; i++) coord[i] = help_pointer + i;
  }



template <typename T, typename K>
void StructDataVector<T,K>::FillAndCopy(const T* const vec)
  {
      T* help_pointer = new T[4];

      ushorti i;
      for(i = 0; i < 4; i++) 
      {
          coord[i]    = help_pointer + i;
          (*coord[i]) = vec[i];
      }
  }



template <typename T, typename K>
void StructDataVector<T,K>::FillAndCopy(T** vec)
  {
      T* help_pointer = new T[4];

      ushorti i;
      for(i = 0; i < 4; i++) 
      {
          coord[i]    = help_pointer + i;
          (*coord[i]) = (*vec[i]);
      }
  }



template <typename T, typename K>
void StructDataVector<T,K>::DeleteSecondInstanceOfCoord()
  {
      if(ref_or_copy == RC::COPY)
      {
          delete[] coord[0]; // coord[0] is pointer on new T[4] 

          ushorti i;
          for(i = 0; i < 4; i++) coord[i] = nullptr;
      } 
  }



template <typename T, typename K>
bool StructDataVector<T,K>::DeleteFirstInstanceOfCoord(DELETE w)
  {
      if(w == DELETE::SOFT)
      {
          ushorti i;
          for(i = 0; i < 4; i++) if(coord[i] != nullptr) return false;
          delete[] coord;

      } else delete[] coord;

      coord = nullptr;

      return true;
  }


} // end namespace Math
